#pragma once
#include <iostream>
#include "Socket.hpp"
#include <pthread.h>
#include <fstream>
#include <sstream>
#include <unordered_map>
#include <vector>
#include "Log.hpp"
const std::string wwwroot = "./wwwroot";
const std::string sep = "\r\n";
const std::string homepage = "/index.html";
class Httpserver;
struct threadData
{
    threadData(int sockfd, Httpserver *s) : sockfd_(sockfd), rsv(s)
    {
    }
    int sockfd_;
    Httpserver *rsv;
};
class RequestServer
{
public:
    void Deserialize(std::string s)
    {
        while (true)
        {
            size_t pos = s.find(sep);
            if (pos == std::string::npos)
                break;
            std::string temp = s.substr(0, pos);
            if (temp.empty())
                break;
            req_handler.push_back(temp);
            s.erase(0, pos + sep.size());
        }
        test = s;
    }
    void Prase()
    {
        std::stringstream ss(req_handler[0]);
        ss >> method >> url >> request_version;
        file_path = wwwroot;
        if (url == "/" || url == "/index.html")
            file_path += homepage;
        else
            file_path += url;
        auto pos = url.rfind(".");
        if (pos != std::string::npos)
            suffix = url.substr(pos);
        else
            suffix = ".html";
    }
    void DebugPrint()
    {
        std::cout << "------------------" << std::endl;
        for (const auto &e : req_handler)
        {
            std::cout << e << "\n\n";
        }
        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "request_version: " << request_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;
    }

public:
    std::vector<std::string> req_handler;
    std::string test;
    std::string method;
    std::string url;
    std::string request_version;
    std::string file_path;
    std::string suffix;
};
class Httpserver
{
    const static int defaultport = 8080;

public:
    Httpserver(uint16_t port = defaultport) : port_(port)
    {
        dicts_.insert({".html", "text/html"});
        dicts_.insert({".png", "image/png"});
    }
    bool Start()
    {
        int opt = 1;
        listensock_.Socket();
        setsockopt(listensock_.Fd(), SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));

        listensock_.Bind(port_);
        listensock_.Listen();
        // sockfd为需要端口复用的套接字
        while (true)
        {
            uint16_t clientport;
            std::string clientip;
            int sockfd = listensock_.Accept(&clientip, &clientport);
            if (sockfd < 0)
            {
                continue;
            }
            lg(Info, "get a new link,sockfd:%d", sockfd);
            threadData *tid = new threadData(sockfd, this);
            pthread_t td;
            pthread_create(&td, nullptr, pthreadRun, tid);
        }
    }
    static std::string ReadHtmlContent(const std::string &filename)
    {
        std::string content;

        auto pos = filename.rfind(".");
        std::string s = filename.substr(pos);
        if (s == ".html")
        {
            std::ifstream in(filename);

            if (!in.is_open())
                return "";
            std::string line;
            while (std::getline(in, line))
            {
                content += line;
            }
            in.close();
        }
        else
        {
            std::ifstream in(filename, std::ios::binary);

            if (!in.is_open())
                return "";

            in.seekg(0, std::ios_base::end);
            auto len = in.tellg();
            in.seekg(0, std::ios_base::beg);
            std::string content;
            content.resize(len);
            in.read((char *)content.c_str(), content.size());

            in.close();
        }
        return content;
    }
    std::string SuffixTostring(const std::string &s)
    {
        auto pos = dicts_.find(s);
        if (pos != dicts_.end())
            return dicts_[".html"];
        return dicts_[s];
    }
    void Handler(int sockfd)
    {
        char buff[10240];
        ssize_t n = recv(sockfd, buff, sizeof(buff) - 1, 0);
        if (n > 0)
        {
            buff[n] = 0;
            std::cout << buff << std::endl;
            RequestServer req;
            req.Deserialize(buff);
            req.Prase();
            req.DebugPrint();
            bool ok = true;
            std::string test = ReadHtmlContent(req.file_path);
            if (test.empty())
            {
                ok = false;
                std::string temp = wwwroot;
                temp += "/";
                temp += "err.html";
                test = ReadHtmlContent(temp);
            }
            std::string response_line;
            if (ok)
                response_line = "HTTP/1.0 200 OK\r\n";
            else
                response_line = "HTTP/1.0 404 Not Found\r\n";
            // response_line = "HTTP/1.0 302 Found\r\n";
            std::string blank_line = "\r\n";
            std::string content_line = "Content-Length: ";
            content_line += std::to_string(test.size());
            content_line += "\r\n";
            // content_line += "Location: https://www.qq.com\r\n";
            content_line += "Content-Type: ";
            content_line += SuffixTostring(req.suffix);
            content_line += "\r\n";
            content_line += "Set-Cookie: name-wzy;password-12345";
            content_line += "\r\n";
            std::string response = response_line;
            response += content_line;
            response += blank_line;
            response += test;
            send(sockfd, response.c_str(), response.size(), 0);
        }
        close(sockfd);
    }
    static void *pthreadRun(void *args)
    {
        pthread_detach(pthread_self());
        threadData *td = static_cast<threadData *>(args);
        td->rsv->Handler(td->sockfd_);
        delete td;
        return nullptr;
    }
    ~Httpserver() {}

private:
    uint16_t port_;
    Sock listensock_;
    std::unordered_map<std::string, std::string> dicts_;
};